home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Very Best of Atari Inside
/
The Very Best of Atari Inside 1.iso
/
mint
/
mint110s
/
debug.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-09-27
|
14KB
|
639 lines
/*
Copyright 1990,1991,1992 Eric R. Smith.
Copyright 1992,1993 Atari Corporation.
All rights reserved.
*/
/* MiNT debugging output routines */
/* also, ksprintf is put here, for lack of any better place to put it */
#include "mint.h"
#include <stdarg.h>
static void VDEBUGOUT P_((int, const char *, va_list));
/*
* ksprintf implements a very crude sprintf() function that provides only
* what MiNT needs...
*
* NOTE: this sprintf probably doesn't conform to any standard at
* all. It's only use in life is that it won't overflow fixed
* size buffers (i.e. it won't try to write more than SPRINTF_MAX
* characters into a string)
*/
static int
PUTC(char *p, int c, int *cnt, int width) {
int put = 1;
if (*cnt <= 0) return 0;
*p++ = c;
*cnt -= 1;
while (*cnt > 0 && --width > 0) {
*p++ = ' ';
*cnt -= 1;
put++;
}
return put;
}
static int
PUTS(char *p, const char *s, int *cnt, int width) {
int put = 0;
if (s == 0) s = "(null)";
while (*cnt > 0 && *s) {
*p++ = *s++;
put++;
*cnt -= 1;
width--;
}
while (width-- > 0 && *cnt > 0) {
*p++ = ' ';
put++;
*cnt -= 1;
}
return put;
}
static int
PUTL(char *p, unsigned long u, int base, int *cnt, int width, int fill_char)
{
int put = 0;
static char obuf[32];
char *t;
t = obuf;
do {
*t++ = "0123456789ABCDEF"[u % base];
u /= base;
width--;
} while (u > 0);
while (width-- > 0 && *cnt > 0) {
*p++ = fill_char;
put++;
*cnt -= 1;
}
while (*cnt > 0 && t != obuf) {
*p++ = *--t;
put++;
*cnt -= 1;
}
return put;
}
int
vksprintf(char *buf, const char *fmt, va_list args)
{
char *p = buf, c, fill_char;
char *s_arg;
int i_arg;
long l_arg;
int cnt;
int width, long_flag;
cnt = SPRINTF_MAX - 1;
while( (c = *fmt++) != 0 ) {
if (c != '%') {
p += PUTC(p, c, &cnt, 1);
continue;
}
c = *fmt++;
width = 0;
long_flag = 0;
fill_char = ' ';
if (c == '0') fill_char = '0';
while (c && isdigit(c)) {
width = 10*width + (c-'0');
c = *fmt++;
}
if (c == 'l' || c == 'L') {
long_flag = 1;
c = *fmt++;
}
if (!c) break;
switch (c) {
case '%':
p += PUTC(p, c, &cnt, width);
break;
case 'c':
i_arg = va_arg(args, int);
p += PUTC(p, i_arg, &cnt, width);
break;
case 's':
s_arg = va_arg(args, char *);
p += PUTS(p, s_arg, &cnt, width);
break;
case 'd':
if (long_flag) {
l_arg = va_arg(args, long);
} else {
l_arg = va_arg(args, int);
}
if (l_arg < 0) {
p += PUTC(p, '-', &cnt, 1);
width--;
l_arg = -l_arg;
}
p += PUTL(p, l_arg, 10, &cnt, width, fill_char);
break;
case 'o':
if (long_flag) {
l_arg = va_arg(args, long);
} else {
l_arg = va_arg(args, unsigned int);
}
p += PUTL(p, l_arg, 8, &cnt, width, fill_char);
break;
case 'x':
if (long_flag) {
l_arg = va_arg(args, long);
} else {
l_arg = va_arg(args, unsigned int);
}
p += PUTL(p, l_arg, 16, &cnt, width, fill_char);
break;
case 'u':
if (long_flag) {
l_arg = va_arg(args, long);
} else {
l_arg = va_arg(args, unsigned int);
}
p += PUTL(p, l_arg, 10, &cnt, width, fill_char);
break;
}
}
*p = 0;
return (int)(p - buf);
}
int ARGS_ON_STACK
ksprintf(char *buf, const char *fmt, ...)
{
va_list args;
int foo;
va_start(args, fmt);
foo = vksprintf(buf, fmt, args);
va_end(args);
return foo;
}
int debug_level = 1; /* how much debugging info should we print? */
int out_device = 2; /* BIOS device to write errors to */
/*
* out_next[i] is the out_device value to use when the current
* device is i and the user hits F3.
* Cycle is CON -> PRN -> AUX -> MIDI -> 6 -> 7 -> 8 -> 9 -> CON
* (Note: BIOS devices 6-8 exist on Mega STe and TT, 9 on TT.)
*
* out_device and this table are exported to bios.c and used here in HALT().
*/
/* 0 1 2 3 4 5 6 7 8 9 */
char out_next[] = { 1, 3, 0, 6, 0, 0, 7, 8, 9, 2 };
/*
* debug log modes:
*
* 0: no logging.
* 1: log all messages, dump the log any time something happens at
* a level that gets shown. Thus, if you're at debug_level 2,
* everything is logged, and if something at levels 1 or 2 happens,
* the log is dumped.
*
* LB_LINE_LEN is 20 greater than SPRINTF_MAX because up to 20 bytes
* are prepended to the buffer string passed to ksprintf.
*/
#define LBSIZE 50 /* number of lines */
#define LB_LINE_LEN (SPRINTF_MAX+20) /* width of a line */
int debug_logging;
int logptr;
static char logbuf[LBSIZE][LB_LINE_LEN];
static short logtime[LBSIZE]; /* low 16 bits of 200Hz: timestamp of msg */
/*
* Extra terse settings - don't even output ALERTs unless asked to.
*
* Things that happen in on an idle Desktop are at LOW_LEVEL:
* Psemaphore, Pmsg, Syield.
*/
#define FORCE_LEVEL 0
#define ALERT_LEVEL 1
#define DEBUG_LEVEL 2
#define TRACE_LEVEL 3
#define LOW_LEVEL 4
/*
* The inner loop does this: at each newline, the keyboard is polled. If
* you've hit a key, then it's checked: if it's ctl-alt, do_func_key is
* called to do what it says, and that's that. If not, then you pause the
* output. If you now hit a ctl-alt key, it gets done and you're still
* paused. Only hitting a non-ctl-alt key will get you out of the pause.
* (And only a non-ctl-alt key got you into it, too!)
*
* When out_device isn't the screen, number keys give you the same effects
* as function keys. The only way to get into this code, however, is to
* have something produce debug output in the first place! This is
* awkward: Hit a key on out_device, then hit ctl-alt-F5 on the console so
* bios.c will call DUMPPROC, which will call ALERT, which will call this.
* It'll see the key you hit on out_device and drop you into this loop.
* CTL-ALT keys make BIOS call do_func_key even when out_device isn't the
* console.
*/
void
debug_ws(s)
const char *s;
{
long key;
int scan;
int stopped;
while (*s) {
(void)Bconout(out_device, *s);
while (*s == '\n' && out_device != 0 && Bconstat(out_device)) {
stopped = 0;
while (1) {
if (out_device == 2) {
/* got a key; if ctl-alt then do it */
if ((Kbshift(-1) & 0x0c) == 0x0c) {
key = Bconin(out_device);
scan = (int) (((key >> 16) & 0xff));
do_func_key(scan);
goto ptoggle;
}
}
else {
key = Bconin(out_device);
if (key < '0' || key > '9') {
ptoggle: /* not a func key */
if (stopped) break;
else stopped = 1;
}
else {
/* digit key from debug device == Fn */
if (key == '0') scan = 0x44;
else scan = (int) (key - '0' + 0x3a);
do_func_key(scan);
}
}
}
}
s++;
}
}
/*
* _ALERT(s) returns 1 for success and 0 for failure.
* It attempts to write the string to "the alert pipe," u:\pipe\alert.
* If the write fails because the pipe is full, we "succeed" anyway.
*
* This is called in vdebugout and also in memprot.c for memory violations.
* It's also used by the Salert() system call in dos.c.
*/
int
_ALERT(s)
char *s;
{
FILEPTR *f;
char alertbuf[SPRINTF_MAX+10], *ptr, *lastspace;
int counter;
char *alert;
int olddebug = debug_level;
int oldlogging = debug_logging;
/* temporarily reduce the debug level, so errors finding
* u:\pipe\alert don't get reported
*/
debug_level = debug_logging = 0;
f = do_open("u:\\pipe\\alert",(O_WRONLY | O_NDELAY),0,(XATTR *)0);
debug_level = olddebug;
debug_logging = oldlogging;
if (f) {
/*
* format the string into an alert box
*/
if (*s == '[') { /* already an alert */
alert = s;
} else {
alert = alertbuf;
ksprintf(alertbuf, "[1][%s", s);
/*
* make sure no lines exceed 30 characters; also, filter out any
* reserved characters like '[' or ']'
*/
ptr = alertbuf+4;
counter = 0;
lastspace = 0;
while(*ptr) {
if (*ptr == ' ') {
lastspace = ptr;
} else if (*ptr == '[') {
*ptr = '(';
} else if (*ptr == ']') {
*ptr = ')';
} else if (*ptr == '|') {
*ptr = ':';
}
if (counter++ >= 29) {
if (lastspace) {
*lastspace = '|';
counter = (int) (ptr - lastspace);
lastspace = 0;
} else {
*ptr = '|';
counter = 0;
}
}
ptr++;
}
strcpy(ptr, "][ OK ]");
}
(*f->dev->write)(f,alert,(long)strlen(alert)+1);
do_close(f);
return 1;
}
else return 0;
}
static void
VDEBUGOUT(level, s, args)
int level;
const char *s;
va_list args;
{
char *lp;
char *lptemp;
logtime[logptr] = (short)(*(long *)0x4baL);
lp = logbuf[logptr];
if (++logptr == LBSIZE) logptr = 0;
if (curproc) {
ksprintf(lp,"pid %3d (%s): ", curproc->pid, curproc->name);
lptemp = lp+strlen(lp);
}
else {
lptemp = lp;
}
vksprintf(lptemp, s, args);
/* for alerts, try the alert pipe unconditionally */
if (level == ALERT_LEVEL && _ALERT(lp)) return;
if (debug_level >= level) {
debug_ws(lp);
debug_ws("\r\n");
}
}
void ARGS_ON_STACK Tracelow(const char *s, ...)
{
va_list args;
if (debug_logging || (debug_level >= LOW_LEVEL)) {
va_start(args, s);
VDEBUGOUT(LOW_LEVEL, s, args);
va_end(args);
}
}
void ARGS_ON_STACK Trace(const char *s, ...)
{
va_list args;
if (debug_logging || (debug_level >= TRACE_LEVEL)) {
va_start(args, s);
VDEBUGOUT(TRACE_LEVEL, s, args);
va_end(args);
}
}
void ARGS_ON_STACK Debug(const char *s, ...)
{
va_list args;
if (debug_logging || (debug_level >= DEBUG_LEVEL)) {
va_start(args, s);
VDEBUGOUT(DEBUG_LEVEL, s, args);
va_end(args);
}
if (debug_logging && debug_level >= DEBUG_LEVEL) DUMPLOG();
}
void ARGS_ON_STACK ALERT(const char *s, ...)
{
va_list args;
if (debug_logging || debug_level >= ALERT_LEVEL) {
va_start(args, s);
VDEBUGOUT(ALERT_LEVEL, s, args);
va_end(args);
}
if (debug_logging && debug_level >= ALERT_LEVEL) DUMPLOG();
}
void ARGS_ON_STACK FORCE(const char *s, ...)
{
va_list args;
va_start(args, s);
VDEBUGOUT(FORCE_LEVEL, s, args);
va_end(args);
/* don't dump log here - hardly ever what you mean to do. */
}
void
DUMPLOG()
{
char *end;
char *start;
short *timeptr;
char timebuf[6];
/* logbuf[logptr] is the oldest string here */
end = start = logbuf[logptr];
timeptr = &logtime[logptr];
do {
if (*start) {
ksprintf(timebuf,"%04x ",*timeptr);
debug_ws(timebuf);
debug_ws(start);
debug_ws("\r\n");
*start = '\0';
}
start += LB_LINE_LEN;
timeptr++;
#ifdef LATTICE
#pragma ignore 83 /* [reference beyond object size] */
#endif
if (start == logbuf[LBSIZE]) {
#ifdef LATTICE
#pragma warning 83 /* [reference beyond object size] */
#endif
start = logbuf[0];
timeptr = &logtime[0];
}
} while (start != end);
logptr = 0;
}
/* wait for a key to be pressed */
void
PAUSE()
{
debug_ws("Hit a key\r\n");
(void)Bconin(2);
}
EXITING
void ARGS_ON_STACK FATAL(const char *s, ...)
{
va_list args;
va_start(args, s);
VDEBUGOUT(-1, s, args);
va_end(args);
if (debug_logging) {
DUMPLOG();
}
HALT();
}
static const char *rebootmsg[MAXLANG] = {
"FATAL ERROR. You must reboot the system.\r\n",
"FATALER FEHLER. Das System muß neu gestartet werden.\r\n", /* German */
"FATAL ERROR. You must reboot the system.\r\n", /* French */
"FATAL ERROR. You must reboot the system.\r\n", /* UK */
"FATAL ERROR. You must reboot the system.\r\n", /* Spanish */
"FATAL ERROR. You must reboot the system.\r\n" /* Italian */
};
EXITING
void HALT()
{
long r;
long key;
int scan;
extern long tosssp; /* in main.c */
#ifdef PROFILING
extern EXITING _exit P_((int));
#endif
restr_intr(); /* restore interrupts to normal */
#ifdef DEBUG_INFO
debug_ws("Fatal MiNT error: adjust debug level and hit a key...\r\n");
#else
debug_ws(rebootmsg[gl_lang]);
#endif
sys_q[READY_Q] = 0; /* prevent context switches */
for(;;) {
/* get a key; if ctl-alt then do it, else halt */
key = Bconin(out_device);
if ((key & 0x0c000000L) == 0x0c000000L) {
scan = (int) ((key >> 16) & 0xff);
do_func_key(scan);
}
else {
break;
}
}
for(;;) {
debug_ws(rebootmsg[gl_lang]);
r = Bconin(2);
if ( (r & 0x0ff) == 'x' ) {
extern int no_mem_prot;
close_filesys();
if (!no_mem_prot)
restr_mmu();
restr_screen();
(void)Super((void *)tosssp); /* gratuitous (void *) for Lattice */
#ifdef PROFILING
_exit(0);
#else
Pterm0();
#endif
}
}
}
/* some key definitions */
#define CTRLALT 0xc
#define DEL 0x53 /* scan code of delete key */
#define UNDO 0x61 /* scan code of undo key */
void
do_func_key(scan)
int scan;
{
extern struct tty con_tty;
switch (scan) {
case DEL:
reboot();
break;
case UNDO:
killgroup(con_tty.pgrp, SIGQUIT);
break;
#ifdef DEBUG_INFO
case 0x3b: /* F1: increase debugging level */
debug_level++;
break;
case 0x3c: /* F2: reduce debugging level */
if (debug_level > 0)
--debug_level;
break;
case 0x3d: /* F3: cycle out_device */
out_device = out_next[out_device];
break;
case 0x3e: /* F4: set out_device to console */
out_device = 2;
break;
case 0x3f: /* F5: dump memory */
DUMP_ALL_MEM();
break;
case 0x58: /* shift+F5: dump kernel allocated memory */
NALLOC_DUMP();
break;
case 0x40: /* F6: dump processes */
DUMPPROC();
break;
case 0x41: /* F7: toggle debug_logging */
debug_logging ^= 1;
break;
case 0x42: /* F8: dump log */
DUMPLOG();
break;
case 0x43: /* F9: dump the global memory table */
QUICKDUMP();
break;
case 0x5c: /* shift-F9: dump the mmu tree */
BIG_MEM_DUMP(1,curproc);
break;
case 0x44: /* F10: do an annotated dump of memory */
BIG_MEM_DUMP(0,0);
break;
#endif
}
}